package com.tatesuke.automine.solver;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Random;
import java.util.Set;

import com.tatesuke.automine.board.Board;
import com.tatesuke.automine.board.CellState;

//TODO 数で判定

public class Solver {

	private Random random;
	private Board board;

	private Set<Coordinate> nextOpenSet;
	private Set<Coordinate> nextFlagSet;

	public Solver(Board board) {
		this.board = board;
	}

	public void solve() {
		nextOpenSet = new HashSet<Coordinate>();
		nextFlagSet = new HashSet<Coordinate>();

		solveByDp();

		if (nextOpenSet.isEmpty() && nextFlagSet.isEmpty()) {
			solveByRandom();
		}
	}

	private void solveByDp() {
		for (int row = 0; row < board.getRowSize(); row++) {
			for (int column = 0; column < board.getColumnSize(); column++) {
				if (!nextOpenSet.isEmpty() || !nextFlagSet.isEmpty()) {
					return;
				}
				solveByDPAt(row, column);
			}
		}
	}

	private void solveByDPAt(int row, int column) {
		if (board.getCellState(row, column) != CellState.OPEN) {
			return;
		}

		int hint = board.getHint(row, column);
		int knownNeighborBombCount = countNeighbor(CellState.FLAG, row, column);
		int unknownNeighborCellsCount = countNeighbor(CellState.CLOSE, row,
				column);

		if (hint == (unknownNeighborCellsCount + knownNeighborBombCount)) {
			nextFlagSet.addAll(getNeighborUnknownList(row, column));
			return;
		}
		if (hint == knownNeighborBombCount) {
			nextOpenSet.addAll(getNeighborUnknownList(row, column));
			return;
		}
	}

	private int countNeighbor(CellState type, int row, int column) {
		int count = 0;

		for (int i = row - 1; i <= row + 1; i++) {
			for (int j = column - 1; j <= column + 1; j++) {
				if ((i == row) && (j == column)) {
					continue;
				}
				if (board.getCellState(i, j) == type) {
					count++;
				}
			}
		}

		return count;
	}

	private List<Coordinate> getNeighborUnknownList(int row, int column) {
		List<Coordinate> neighborUnknwonCellList = new ArrayList<Coordinate>(8);

		for (int i = row - 1; i <= row + 1; i++) {
			for (int j = column - 1; j <= column + 1; j++) {
				if ((i == row) && (j == column)) {
					continue;
				}
				if (board.getCellState(i, j) == CellState.CLOSE) {
					neighborUnknwonCellList.add(new Coordinate(i, j));
				}
			}
		}

		return neighborUnknwonCellList;
	}

	private void solveByRandom() {
		if (random == null) {
			random = new Random();
		}

		int row;
		int column;
		do {
			row = random.nextInt(board.getRowSize());
			column = random.nextInt(board.getColumnSize());
		} while (board.getCellState(row, column) != CellState.CLOSE);

		nextOpenSet.add(new Coordinate(row, column));
	}



	public Set<Coordinate> getNextFlagSet() {
		return nextFlagSet;
	}

	public Set<Coordinate> getNextOpenSet() {
		return nextOpenSet;
	}

	void setRandom(Random random) {
		this.random = random;
	}


}
